https://labuladong.online/algo/data-structure-basic/trie-map-basic/
今天是學習的第 29 天,主要學習了 Trie / 字典樹 / 前綴樹原理:
Trie 樹(也叫做字典樹、前綴樹)是多叉樹結構的延伸,是一種針對字串進行特殊優化的資料結構。
這邊用程式碼舉個例子:
// 建立一棵字典樹
let trie = Trie.create();
// 向字典樹添加多個字串元素
trie.add("that");
trie.add("the");
trie.add("their");
trie.add("apple");
trie.add("them");
最終的字典樹長的就會如下圖,可以看到它沿用了部分重複的節點(如 t
、h
、e
)以節省空間:
Trie 樹是一種針對字串特殊優化的資料結構,它對字串的處理有若干優勢:
一、節省儲存空間
這邊以 HashMap
來對比:在傳統的哈希表中,如果我們要儲存 "apple"
、"app"
、"appl"
這三個字串作為鍵,哈希表會將每個完整的字串都儲存一次。
回想哈希表的實現原理,鍵值對 key-value
會被儲存到 table
陣列中,也就是說它實際創建了三個完整的字串物件,總共佔用了 12 個字符的記憶體空間(apple
5個 + app
3個 + appl
4個)。
相對地,如果使用 Trie 樹來儲存這三個鍵,Trie 樹底層並不會重複儲存公共前綴。它會將相同的前綴部分共享,所以只需要 "apple"
這 5 個字符的記憶體空間就能儲存這三個鍵,大幅節省了儲存空間。
二、方便操作公共前綴
Trie 樹提供了許多方便的前綴操作功能。假設我們在 Trie 樹中儲存了 "that"
、"the"
、"them"
、"apple"
這些鍵,我們可以:
"themxyz"
的最短前綴是 "the"
"themxyz"
的最長前綴是 "them"
"tha"
會返回 true
(因為有 "that"
),但檢查 "thz"
會返回 false
"th"
為前綴的鍵有 "that"
、"the"
、"them"
這些前綴操作的時間複雜度都是 O(L),其中 L 是前綴字串的長度。唯一的例外是查找所有符合某前綴的鍵,這個操作的複雜度取決於返回結果的數量。
三、可以使用通配符
Trie 樹支持通配符匹配,可以使用 .
符號來匹配任意單個字符。
這個特性讓我們能夠進行模糊搜尋,例如在存儲了 "that"
、"the"
、"team"
、"zip"
等鍵的 Trie 樹中,我們可以用 "t.a."
這個模式來找出所有符合「t 開頭、第三個字符是 a、總共四個字符」的鍵,會找到 "team" 和 "that"。同樣地,用 ".ip" 可以匹配到 "zip",因為它符合「任意字符 + ip」的模式。
四、可以按照字典序遍歷鍵
Trie 樹的結構天然支持按照字典序(alphabetical order)來遍歷所有的鍵。
這是因為 Trie 樹在儲存字符時,會按照字符的編碼順序來組織子節點,所以當我們遍歷整棵樹時,自然就會按照字典序輸出所有的鍵。
例如,一個包含 "that"
、"the"
、"them"
、"zip"
、"apple"
的 Trie 樹,遍歷時會依序得到 "apple"
、"that"
、"the"
、"them"
、"zip"
。
Trie 樹本質上就是一棵從二叉樹衍生出來的多叉樹。
讓我們從熟悉的樹結構開始理解:
val
,以及指向左右子節點的指針 left
和 right
// 基本的二叉樹節點
var TreeNode = function() {
var val;
var left;
var right;
};
val
,以及一個 children
陣列來儲存多個子節點的指針// 基本的多叉樹節點
var TreeNode = function() {
this.val = 0;
this.children = [];
};
而 Trie 樹節點的結構則有其特殊之處:每個節點包含 val
欄位(儲存鍵對應的值)和一個固定大小的 children
陣列(通常是 256,對應 ASCII 字符集)。
// Trie 樹節點實現
var TrieNode = function() {
this.val = null;
this.children = new Array(256);
};
Trie 樹節點最關鍵的特性是:children
陣列的索引具有特殊意義,它代表一個字符。
例如 children[97]
如果非空,就表示這裡儲存了字符 'a'
(因為 'a'
的 ASCII 碼是 97)。
這個陣列大小可以根據實際需求調整:
a-z
,可以設為 26HashMap<Character, TrieNode>
來替代陣列,效果相同理解 Trie 樹結構的關鍵:
children
陣列中的索引位置」來確定的在 Trie 樹的視覺化表示中:
.
作為萬用字元進行模糊搜尋,方便進行模式匹配children
陣列(通常 256 個),陣列索引代表字符的 ASCII 碼children
陣列的索引位置來確定